
#include <BScene>
#include <BImage>
#include "ReadWrite_3ds.h"

ReadWrite_3ds::ReadWrite_3ds()
{
	_author = "zym";
	_version = "1.0";
	_description = "This is BWE Plugin for reading *.jpg image file, write width libjpeg, thank a lot !";
}
ReadWrite_3ds::~ReadWrite_3ds()
{

}

bool ReadWrite_3ds::readObject(const BString& fileName, BObject* object)
{
	_mates.clear();
	_geoms.clear();

	BNode* node = dynamic_cast<BNode*>(object);
	BScene* scene = dynamic_cast<BScene*>(object);

	if (!node && !scene)
	{
		puts("3ds.brw : object is not node or scene.");
		return false;
	}

	if(!_file.open(fileName, BFile::Mode_Read))
		return false;

	Chunk chunk;
	readChunk(chunk);
	if(chunk.sign != Chunk::Chunk3DS)
	{
		_file.close();
		return false;
	}
	
	int version = 0;
	Chunk subChunk;
	while(readChunk(subChunk) < chunk.end)
	{
		BString text = subChunk.sign;
		switch(subChunk.sign)
		{
		case Chunk::ChunkVersion:
			_file.read(version);
			break;
		
		case Chunk::ChunkEditor:
			readChunkEditor(subChunk);
			break;

		case Chunk::ChunkFramer:
			readChunkFramer(subChunk);
			break;
		
		default:
			puts(text.cstr());
			break;
		}
		passChunk(subChunk);
	}
	
	_file.close();

	if (_geoms.empty())
		return false;

	for (auto it = _geoms.begin(); it != _geoms.end(); ++it)
	{
		Mesh3DS* m = it->ptr();
		BMesh* mesh = m->mesh;
		mesh->smooth();

		Mate3DS* mate = findMaterial(m->matName);
		if (mate)
		{
			mesh->setMaterial(mate->material);
			mesh->setTexture(mate->texture);
		}
	}

	if (node)
	{
		for(auto it = _geoms.begin(); it != _geoms.end(); ++it)
		{
			Mesh3DS* m = it->ptr();
			BMesh* mesh = m->mesh;
			node->addShape(mesh);
		}
		return true;
	}

	if (scene)
	{
		for (auto it = _geoms.begin(); it != _geoms.end(); ++it)
		{
			Mesh3DS* m = it->ptr();
			BMesh* mesh = m->mesh;
			scene->inserts(mesh);
		}
		return true;
	}

	return false;
}
bool ReadWrite_3ds::writeObject(const BString& fileName, const BObject* object)
{
	return false;
}

const BString& ReadWrite_3ds::author() const
{
	return _author;
}
const BString& ReadWrite_3ds::version() const
{
	return _version;
}
const BString& ReadWrite_3ds::description() const
{
	return _description;
}
const BString& ReadWrite_3ds::log() const
{
	return _log;
}

int ReadWrite_3ds::readChunk(Chunk& chunk)
{
	chunk.begin = _file.offset();
	chunk.sign = 0;
	chunk.size = 0;
	chunk.end = 0;

	if(0 == _file.read(chunk.sign))
		return chunk.begin;

	if (0 == _file.read(chunk.size))
		return chunk.begin;

	chunk.end = chunk.begin + chunk.size;

	return chunk.begin;
}
bool ReadWrite_3ds::passChunk(const Chunk& chunk)
{
	return _file.seek(chunk.end);
}

bool ReadWrite_3ds::readChunkEditor(const Chunk& chunk)
{
	Chunk subChunk;
	while (readChunk(subChunk) < chunk.end)
	{
		switch(subChunk.sign)
		{
		case Chunk::ChunkMaterial:
			readChunkMaterial(subChunk);
			break;
		
		case Chunk::ChunkMesh:
			readChunkMesh(subChunk);
			break;

		default:
			break;
		}
		passChunk(subChunk);
	}
	return true;
}
bool ReadWrite_3ds::readChunkFramer(const Chunk& chunk)
{
	return true;
}

void ReadWrite_3ds::readChunkMaterial(const Chunk& chunk)
{
	Mate3DS* mate = new Mate3DS;
	mate->material = new BMaterial;
	_mates.append(mate);

	BColor ambient, diffuse, specular;
	float shininess = 1.0f;
	float shininessStrength = 1.0f;
	float transparency = 1.0f;
	float transparencyFalloff = 1.0f;
	float reflection = 1.0f;

	Chunk subChunk;
	while (readChunk(subChunk) < chunk.end)
	{
		BString text = subChunk.sign;
		
		char buffer[256];
		switch(subChunk.sign)
		{
		case Chunk::ChunkMaterialName:
			_file.readString(buffer, 256);
			mate->material->setName(buffer);
			break;

		case Chunk::ChunkMaterialAmbient:
			ambient = readChunkColor(subChunk);
			break;

		case Chunk::ChunkMaterialDiffuse:
			diffuse = readChunkColor(subChunk);
			break;

		case Chunk::ChunkMaterialSpecular:
			specular = readChunkColor(subChunk);
			break;

		case Chunk::ChunkMaterialShininess:
			shininess = readChunkPercent(subChunk);
			break;

		case Chunk::ChunkMaterialShininessStrength:
			shininessStrength = readChunkPercent(subChunk);
			break;

		case Chunk::ChunkMaterialTransparency:
			transparency = readChunkPercent(subChunk);
			break;

		case Chunk::ChunkMaterialTransparencyFalloff:
			transparencyFalloff = readChunkPercent(subChunk);
			break;

		case Chunk::ChunkMaterialReflection:
			reflection = readChunkPercent(subChunk);
			break;

		case Chunk::ChunkMaterialTextureMin:
			readChunkMaterialTexture(subChunk, mate);
			break;
			
		case Chunk::ChunkMaterialTextureMax:
			break;

		default:
			puts(buffer);
			break;
		}
		passChunk(subChunk);
	}

	mate->material->setAmbient(ambient);
	mate->material->setDiffuse(diffuse);
	mate->material->setSpecular(specular);
	//mate->material->setShininess(shininess);
	printf("transparencyFalloff : %f\n", transparencyFalloff);
}
bool ReadWrite_3ds::readChunkMesh(const Chunk& chunk)
{
	Mesh3DS* geom = new Mesh3DS;
	BMesh* mesh = new BMesh();
	geom->mesh = mesh;
	_geoms.append(geom);

	char geomName[256];
	_file.readString(geomName, 256);
	geom->mesh->setName(geomName);
	puts(geomName);

	bool meshHidden = false;

	Chunk subChunk;
	while (readChunk(subChunk) < chunk.end)
	{
		switch(subChunk.sign)
		{
		case Chunk::ChunkMeshHidden:
			{
				meshHidden = true;
			}
			break;
		
		case Chunk::ChunkMeshMesh:
		{
			if (!readChunkMeshMesh(subChunk, geom))
				return false;
		}
		default:
			break;
		}
		passChunk(subChunk);
	}
	return true;
}

void ReadWrite_3ds::readChunkMaterialTexture(const Chunk& chunk, Mate3DS* material)
{
	Chunk subChunk;
	while (readChunk(subChunk) < chunk.end)
	{
		BString text = subChunk.sign;

		switch(subChunk.sign)
		{
		case Chunk::ChunkTextureFileName:
		{
			char fileName[256];
			_file.readString(fileName, 256);
			BString path = _file.fileName().path();
			BImage* image = Read<BImage>(path + '/' + fileName);
			if (image)
			{
				image->setWrap(BTexture::Wrap_Repeat);
				material->texture = image;
				printf("Load texture : %s\n", fileName);
			}
			else
			{
				printf("Miss texture : %s\n", fileName);
			}

			break;
		}
		case Chunk::ChunkTextureParament:
		{
			int size = subChunk.size;
			break;
		}
		default:
			break;
		}
		passChunk(subChunk);
	}
}
bool ReadWrite_3ds::readChunkMeshMesh(const Chunk& chunk, Mesh3DS* geom)
{
	Chunk subChunk;
	while (readChunk(subChunk) < chunk.end)
	{
		BString text = subChunk.sign;

		switch (subChunk.sign)
		{
		case Chunk::ChunkMeshLocalCoordSystem:
		{
			float mat[4][3];
			_file.read(&mat, 12 * sizeof(float));

			BMatrix matrix;
			//matrix.set(0, mat[0][0], mat[0][1], mat[0][2], 0);
			//matrix.set(1, mat[1][0], mat[1][1], mat[1][2], 0);
			//matrix.set(2, mat[2][0], mat[2][1], mat[2][2], 0);
			//matrix.set(3, mat[3][0], mat[3][1], mat[3][2], 1);

			matrix.set(0, mat[0][0], mat[1][0], mat[2][0], 0);
			matrix.set(1, mat[0][1], mat[1][1], mat[2][1], 0);
			matrix.set(2, mat[0][2], mat[1][2], mat[2][2], 0);
			matrix.set(3, mat[0][3], mat[1][3], mat[2][3], 1);


			geom->mesh->setMatrix(matrix);
			break;
		}
		case Chunk::ChunkMeshVertices:
		{
			unsigned short count;
			_file.read(count);

			BVectorArray* vertices = new BVectorArray;
			for (int i = 0; i < count; i++)
			{
				float x, y, z;
				_file.read(x);
				_file.read(y);
				_file.read(z);

				vertices->append(BVector(x, y, z));
			}
			geom->mesh->setVertices(vertices);
			break;
		}
		case Chunk::ChunkMeshFaces:
		{
			unsigned short count;
			_file.read(count);

			BFaceArray* faces = new BFaceArray(count);

			unsigned short ia;
			unsigned short ib;
			unsigned short ic;
			unsigned short unused;
			for (int i = 0; i < count; i++)
			{
				_file.read(ia);
				_file.read(ib);
				_file.read(ic);
				(*faces)[i].set(ia, ib, ic);
				_file.read(unused);
			}

			geom->mesh->setFaces(faces);

			Chunk subsubChunk;
			while (readChunk(subsubChunk) < subChunk.end)
			{
				switch (subsubChunk.sign)
				{
				case Chunk::ChunkMeshFacesMatlist:
					_file.readString(geom->matName);
					break;

				default:
					break;
				}
				passChunk(subsubChunk);
			}
			break;
		}
		case Chunk::ChunkMeshTexCoords:
		{
			unsigned short count;
			_file.read(count);

			BCoordArray* texCoords = new BCoordArray(count);
			BCoord coord;
			for (int i = 0; i < count; i++)
			{
				_file.read(coord);
				(*texCoords)[i] = coord;
			}
			geom->mesh->setTexCoords(texCoords);
		break;
		}
		default:
			puts(text.cstr());
			break;
		}
		passChunk(subChunk);
	}
	return geom->mesh->vertices() != 0;
}

BColor ReadWrite_3ds::readChunkColor(const Chunk& chunk)
{
	BColor color;
	unsigned char rb;
	unsigned char gb;
	unsigned char bb;

	Chunk subChunk;
	while (readChunk(subChunk) < chunk.end)
	{
		switch(subChunk.sign)
		{
		case Chunk::ChunkColorb:
			_file.read(&rb, 1);
			_file.read(&gb, 1);
			_file.read(&bb, 1);
			color.r() = rb;
			color.g() = gb;
			color.b() = bb;
			color.a() = 255;
			break;
		case Chunk::ChunkColorf:
		case Chunk::ChunkColorbg:
		case Chunk::ChunkColorfg:
			puts("ChunkColorfg color gamma corrected failed");
			break;
		default:
			break;
		}
		passChunk(subChunk);
	}
	return color;
}
float ReadWrite_3ds::readChunkPercent(const Chunk& chunk)
{
	short percents = 100;
	float percentf = 1.0f;

	Chunk subChunk;
	while (readChunk(subChunk) < chunk.end)
	{
		switch(subChunk.sign)
		{
		case Chunk::ChunkPercenti:
			_file.read(percents);
			percentf = percents / 100.0f;
			break;
		case Chunk::ChunkPercentf:
			_file.read(percentf);
			break;
		default:
			break;
		}
		passChunk(subChunk);
	}
	return percentf;
}

Mate3DS* ReadWrite_3ds::findMaterial(const BString& matName)
{
	for(BList<Mate3DSHolder>::iterator it = _mates.begin(); it != _mates.end(); ++it)
	{
		Mate3DS* mate = it->ptr();
		BMaterial* material = mate->material.ptr();
		if(material->name() == matName)
			return mate;
	}
	return 0;
}

